package com.disv.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Console;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.disv.domain.DdCallbackInfo;
import com.disv.service.strategy.DingCallBackStrategy;
import com.disv.support.encrypt.DingCallbackCrypto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

@RequestMapping("bihu/callback")
@RestController
@Slf4j
@RequiredArgsConstructor
@EnableScheduling
public class CallBackController {

    @Value("${dd.callback.suiteKey}")
    private String dingDingSuiteKey;
    @Value("${dd.callback.aesKey}")
    private String dingDingAesKey;
    @Value("${dd.callback.token}")
    private String dingDingToken;

    /**
     * 创建应用，验证回调URL创建有效事件（第一次保存回调URL之前）
     */
    private static final String EVENT_CHECK_CREATE_SUITE_URL = "check_create_suite_url";

    /**
     * 创建应用，验证回调URL变更有效事件（第一次保存回调URL之后）
     */
    private static final String EVENT_CHECK_UPADTE_SUITE_URL = "check_update_suite_url";

    private final String BIZ_DATA = "bizData";

    /**
     * 企业授权开通应用事件
     */
    private static final String EVENT_TMP_AUTH_CODE = "tmp_auth_code";

    @Autowired
    private DingCallBackStrategy dingCallBackStrategy;

    @PostMapping(value = "dingCallback")
    public Object dingCallback(
            @RequestParam(value = "signature") String signature,
            @RequestParam(value = "timestamp") Long timestamp,
            @RequestParam(value = "nonce") String nonce,
            @RequestBody(required = false) JSONObject body
    ) {
        String params = "signature:" + signature + " timestamp:" + timestamp + " nonce:" + nonce + " body:" + body;
        try {
            log.info("begin callback:" + params);
            DingCallbackCrypto dingTalkEncryptor = new DingCallbackCrypto(dingDingToken, dingDingAesKey, this.dingDingSuiteKey);

            // 从post请求的body中获取回调信息的加密数据进行解密处理
            String encrypt = body.getString("encrypt");
            String plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp.toString(), nonce, encrypt);
            JSONObject callBackContent = JSON.parseObject(plainText);

            // 根据回调事件类型做不同的业务处理
            String eventType = callBackContent.getString("EventType");
            if (EVENT_CHECK_CREATE_SUITE_URL.equals(eventType)) {
                log.info("验证新创建的回调URL有效性: " + plainText);
            } else if (EVENT_CHECK_UPADTE_SUITE_URL.equals(eventType)) {
                log.info("验证更新回调URL有效性: " + plainText);
            } else if ("SYNC_HTTP_PUSH_MEDIUM".equals(eventType)) {
                log.info("普通优先级数据: " + plainText);
            } else if ("SYNC_HTTP_PUSH_HIGH".equals(eventType)) {
                log.info("高优先级数据: " + plainText);
            } else if (EVENT_TMP_AUTH_CODE.equals(eventType)) {
                // 本事件应用应该异步进行授权开通企业的初始化，目的是尽最大努力快速返回给钉钉服务端。用以提升企业管理员开通应用体验
                // 即使本接口没有收到数据或者收到事件后处理初始化失败都可以后续再用户试用应用时从前端获取到corpId并拉取授权企业信息，进而初始化开通及企业。
                log.info("企业授权开通应用事件: " + plainText);
            } else {
                // 其他类型事件处理
                log.info("其他事件:" + callBackContent);
            }
            DdCallbackInfo ddCallbackInfo = new DdCallbackInfo();
            ddCallbackInfo.setEventType(eventType);
            ddCallbackInfo.setContent(plainText);
            ddCallbackInfo.setDateCreated(new Date());
            JSONArray bizDataJson = callBackContent.getJSONArray(BIZ_DATA);
            if (CollectionUtil.isNotEmpty(bizDataJson)) {
                bizDataJson.forEach(o -> {
                    BeanUtil.copyProperties(o, ddCallbackInfo);
                    ddCallbackInfo.setBizData(JSON.toJSONString(o));
                    // 处理事件报文
                    dingCallBackStrategy.getResource(ddCallbackInfo.getBizType()).action(ddCallbackInfo.getBizData());
                    // 记录上报事件
                    Console.log(JSON.toJSONString(ddCallbackInfo));
                });
            } else {
                Console.log(JSON.toJSONString(ddCallbackInfo));
            }

            // 返回success的加密信息表示回调处理成功
            log.info(dingTalkEncryptor.getEncryptedMap("success", timestamp, nonce).toString());
            return dingTalkEncryptor.getEncryptedMap("success", timestamp, nonce);
        } catch (Exception e) {
            //失败的情况，应用的开发者应该通过告警感知，并干预修复
            log.error("process callback fail." + params, e);
            return "fail";
        }
    }
}
